package xin.xihc.lottery.controller;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import xin.xihc.lottery.LotteryType;
import xin.xihc.lottery.dao.LotteryNumbersDao;
import xin.xihc.lottery.dto.LotteryGroupVO;
import xin.xihc.lottery.model.LotteryNumbers;
import xin.xihc.lottery.param.AutogenParam;
import xin.xihc.lottery.param.BasicParam;
import xin.xihc.lottery.param.DetailParam;
import xin.xihc.lottery.param.ManualGenNumsParam;
import xin.xihc.lottery.service.LotteryService;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * API
 *
 * @author Leo.Xi
 * @date 2022/2/26
 * @since 1.0
 **/
@RestController
@RequestMapping("/api")
@Validated
public class ApiController {

    @Autowired
    private LotteryNumbersDao lotteryNumbersDao;
    @Autowired
    LotteryService lotteryService;
    public static final int MAX_TIMES = 20000;

    @GetMapping("/init")
    public void init(
            @RequestParam LotteryType type, @RequestParam String stq, @RequestParam String enq) throws IOException {
        String url = "";
        switch (type) {
            case SSQ:
                url = "https://www.cjcp.com.cn/zoushitu/cjwssq/hqzonghe.html";
                break;
            case DLT:
                url = "https://www.cjcp.com.cn/zoushitu/cjwdlt/qqdingwei-01.html";
                break;
        }
        Document document = Jsoup.connect(url)
                                 .data("stq", stq)
                                 .data("enq", enq)
                                 .data("st", "1")
                                 .post();
        Elements children = document.selectFirst("#tabledata #pagedata")
                                    .children();
        lotteryService.addOrUpdateLottery(type, children);
    }

    @GetMapping("/parse")
    public Object parse(@RequestParam LotteryType type) {
        LotteryNumbers lottery = new LotteryNumbers();
        lottery.setType(type);
        List<LotteryNumbers> page = lotteryNumbersDao.page(lottery, null);
        return page;
    }

    @GetMapping("/next")
    public Object next(@RequestParam LotteryType type) {
        LotteryNumbers lottery = new LotteryNumbers();
        lottery.setType(type);
        List<LotteryNumbers> page = lotteryNumbersDao.page(lottery, null);
        return page;
    }

    @GetMapping("/autogen")
    public List<String> autogen(@Validated AutogenParam param) {
        List<String> res = new LinkedList<>();

        List<Map<Integer, LotteryGroupVO>> numVos = new LinkedList<>();
        for (int i = 0; i < 7; i++) {
            DetailParam p = new DetailParam();
            p.setType(param.getType());
            p.setYear(param.getYear());
            p.setQs(param.getQs());
            p.setIndex(i + 1);
            numVos.add(lotteryService.numStat(p, false)
                                     .stream()
                                     .collect(Collectors.toMap(LotteryGroupVO::getNum, x -> x)));
        }
        int times = 0;
        do {
            Integer[] nums = new Integer[7];
            int last = 0;
            for (int i = 0; i < 7; i++) {
                if (LotteryType.DLT.equals(param.getType()) && i == 5) {
                    last = 0;
                }
                if (LotteryType.SSQ.equals(param.getType()) && i == 6) {
                    last = 0;
                }
                nums[i] = lotteryService.nextNum(param.getType(), i + 1, last);
                last    = nums[i];
            }
            // DB check
            String numsFormat = genNums(param.getType(), nums);
            boolean exists = lotteryService.dbExists(param.getType(), numsFormat);
            if (!exists) {
                // 计算得分
                BigDecimal score = BigDecimal.ZERO;
                for (int i = 0; i < nums.length; i++) {
                    Map<Integer, LotteryGroupVO> groupVOMap = numVos.get(i);
                    LotteryGroupVO vo = groupVOMap.get(nums[i]);
                    if (null != vo) {
                        score = score.add(vo.getRate());
                    }
                }
                if (score.compareTo(param.getMinScore()) > 0) {
                    res.add(String.format("号码【%s】  得分：%s", numsFormat, score));
                }
            }
            times++;
            if (times > MAX_TIMES) {
                break;
            }
        } while (res.size() < param.getLimit());
        System.err.printf("本次循环了%s次%n", times);
        return res;
    }

    private String genNums(LotteryType type, Integer[] nums) {
        if (nums.length != 7) {
            return "请输入正确的数字（7位,|分隔）";
        }
        String res;

        switch (type) {
            case SSQ:
                res
                        = String.format("%02d,%02d,%02d,%02d,%02d,%02d|%02d", nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6]);
                break;
            case DLT:
                res
                        = String.format("%02d,%02d,%02d,%02d,%02d|%02d,%02d", nums[0], nums[1], nums[2], nums[3], nums[4], nums[5], nums[6]);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + type);
        }
        return res;
    }

    @GetMapping("/check")
    public String check(@Validated BasicParam param, @RequestParam String nums) {
        String[] splits = nums.split(",");
        if (splits.length != 7) {
            return "请输入正确的数字（7位,|分隔）";
        }
        Integer[] ints = Arrays.asList(splits)
                               .stream()
                               .map(Integer::parseInt)
                               .toArray(Integer[]::new);
        LotteryType type = param.getType();
        nums = genNums(type, ints);
        LotteryNumbers lottery = new LotteryNumbers();
        lottery.setType(type);
        lottery.setNums(nums);
        List<LotteryNumbers> page = lotteryNumbersDao.page(lottery, null);
        if (page.isEmpty()) {
            List<Map<Integer, LotteryGroupVO>> numVos = new LinkedList<>();
            for (int i = 0; i < 7; i++) {
                DetailParam p = new DetailParam();
                p.setType(type);
                p.setYear(param.getYear());
                p.setQs(param.getQs());
                p.setIndex(i + 1);
                numVos.add(lotteryService.numStat(p, false)
                                         .stream()
                                         .collect(Collectors.toMap(LotteryGroupVO::getNum, x -> x)));
            }
            // 计算得分
            BigDecimal score = BigDecimal.ZERO;
            for (int i = 0; i < ints.length; i++) {
                Map<Integer, LotteryGroupVO> groupVOMap = numVos.get(i);
                LotteryGroupVO vo = groupVOMap.get(ints[i]);
                if (null != vo) {
                    score = score.add(vo.getRate());
                }
            }

            return String.format("号码【%s】  得分：%s", nums, score);
        }
        return page.stream()
                   .map(LotteryNumbers::getQs)
                   .collect(Collectors.joining(","));
    }

    @GetMapping("/detail")
    public List<LotteryGroupVO> detail(@Validated DetailParam param) {
        return lotteryService.numStat(param, true);
    }


    @PostMapping("/manual-gen-nums")
    public List<String> manualGenNums(@Validated @RequestBody ManualGenNumsParam param) {
        for (List<String> nums : param.getNums()) {
            if (nums.isEmpty()) {
                throw new RuntimeException("号码不完整");
            }
        }
        List<String> newNums = new LinkedList<>();
        for (List<String> nums : param.getNums()) {
            newNums = recursion(newNums, nums);
        }
        // 获取基础数据
        List<Map<Integer, LotteryGroupVO>> numVos = new LinkedList<>();
        for (int i = 0; i < 7; i++) {
            DetailParam p = new DetailParam();
            p.setType(param.getType());
            p.setYear(param.getYear());
            p.setQs(param.getQs());
            p.setIndex(i + 1);
            numVos.add(lotteryService.numStat(p, false)
                                     .stream()
                                     .collect(Collectors.toMap(LotteryGroupVO::getNum, x -> x)));
        }
        // 得到数据
        List<String> ress = new LinkedList<>();
        Integer[] ints;
        for (String nums : newNums) {
            ints = Arrays.asList(nums.split(","))
                         .stream()
                         .map(Integer::parseInt)
                         .toArray(Integer[]::new);
            // 判断是否规范
            if (!isValid(param.getType(), ints)) {
                continue;
            }
            nums = genNums(param.getType(), ints);
            LotteryNumbers lottery = new LotteryNumbers();
            lottery.setType(param.getType());
            lottery.setNums(nums);
            List<LotteryNumbers> page = lotteryNumbersDao.page(lottery, null);
            if (page.isEmpty()) {
                // 计算得分
                BigDecimal score = BigDecimal.ZERO;
                for (int i = 0; i < ints.length; i++) {
                    Map<Integer, LotteryGroupVO> groupVOMap = numVos.get(i);
                    LotteryGroupVO vo = groupVOMap.get(ints[i]);
                    if (null != vo) {
                        score = score.add(vo.getRate());
                    }
                }

                ress.add(String.format("号码【%s】  得分：%s", nums, score));
            } else {
                ress.add(String.format("号码【%s】  已存在：%s", nums, page.stream()
                                                                   .map(LotteryNumbers::getQs)
                                                                   .collect(Collectors.joining(","))));
            }
        }

        return ress;
    }

    /**
     * 是否有效
     *
     * @param type
     * @param ints
     * @return true-有效
     * @author Leo.Xi
     * @date 2022/3/20
     * @since 0.0.1
     */
    private boolean isValid(LotteryType type, Integer[] ints) {
        int last = 0; // 上一位数值
        for (int i = 0; i < ints.length; i++) {
            if (i == 5 && type.equals(LotteryType.DLT)) {
                last = 0;
            }
            if (i == 6 && type.equals(LotteryType.SSQ)) {
                last = 0;
            }
            if (ints[i] <= last) {// 小于等于都不行
                return false;
            }
            last = ints[i];
        }
        return true;
    }

    /**
     * 递归循环
     *
     * @param num1s
     * @param num2s
     * @return
     * @author Leo.Xi
     * @date 2022/3/20
     * @since 0.0.1
     */
    private List<String> recursion(List<String> num1s, List<String> num2s) {
        List<String> res = new LinkedList<>();
        if (num1s.isEmpty()) {
            return num2s;
        }
        if (num2s.isEmpty()) {
            return num1s;
        }
        for (String num1 : num1s) {
            for (String num2 : num2s) {
                res.add(num1 + "," + num2);
            }
        }
        return res;
    }
}